package com.zlc.workflow.core.process.cmd;

import cn.hutool.core.util.IdUtil;
import com.zlc.common.common.exception.BusinessException;
import com.zlc.common.common.exception.WorkflowException;
import com.zlc.common.core.process.model.vo.*;
import com.zlc.common.core.process.service.*;
import com.zlc.common.util.AssertUtils;
import com.zlc.common.util.BeanCopyUtils;
import com.zlc.common.util.PlatUtils;
import com.zlc.workflow.constant.ProcessStatusEnum;
import com.zlc.workflow.core.process.model.*;
import com.zlc.workflow.core.process.model.design.AntDesignEntity;
import com.zlc.workflow.core.process.model.design.AntDesignUtil;
import com.zlc.workflow.flow.parser.ProcessBuilder;
import com.zlc.workflow.flow.parser.ProcessBuilderFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.ObjectUtils;

import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;

import static com.zlc.common.common.constant.PlatConstant.FLAG_FALSE;
import static com.zlc.common.common.constant.PlatConstant.FLAG_TRUE;
import static com.zlc.common.common.constant.WorkflowConstant.*;

@Service
public class DeployProcessCmd {

    @Autowired
    private StoneProcessService stoneProcessService;
    @Autowired
    private StoneProcessNodeService stoneProcessNodeService;
    @Autowired
    private StoneNodeApproveConfigService stoneNodeApproveConfigService;
    @Autowired
    private StoneNodeApproveUserConfigService stoneNodeApproveUserConfigService;
    @Autowired
    private StoneProcessAuthConfigService stoneProcessAuthConfigService;
    @Autowired
    private StoneEventConfigService stoneEventConfigService;
    @Autowired
    private StoneProcessVariableConfigService stoneProcessVariableConfigService;
    @Autowired
    private StoneProcessFormConfigService stoneProcessFormConfigService;

    public synchronized ProcessDeployEntity deploy(ProcessDeployEntity entity) {
        buildFlowProcess(entity);
        //保存流程信息
        saveProcess(entity);
        //全删全增
        removeProcessConfig(entity);
        //保存流程事件
        saveProcessEvent(entity);
        //保存流程权限配置
        saveProcessAuthConfig(entity);
        //保存流程变量配置
        saveVariableConfig(entity);
        //保存节点配置
        saveNodeConfig(entity);
        return entity;
    }

    private void buildFlowProcess(ProcessDeployEntity entity) {
        AntDesignEntity design = entity.getDesign();
        AssertUtils.isTrue(!ObjectUtils.isEmpty(design), "流程设计信息缺失");
        FlowProcess process = AntDesignUtil.buildFLowProcess(design);
        process.setProcessCode(entity.getProcessVo().getProcessCode());
        process.setProcessName(entity.getProcessVo().getProcessName());
        entity.setProcess(process);
        if (entity.getNodeEntities() == null) {
            entity.setNodeEntities(new ArrayList<>());
        }
        if (entity.getVariableConfigs() == null) {
            entity.setVariableConfigs(new ArrayList<>());
        }
        if (entity.getEventConfigs() == null) {
            entity.setEventConfigs(new ArrayList<>());
        }
        if (entity.getAuthConfigs() == null) {
            entity.setAuthConfigs(new ArrayList<>());
        }
    }

    private void saveVariableConfig(ProcessDeployEntity entity) {
        List<ProcessVariableConfig> variableConfigVos = entity.getVariableConfigs();
        String processId = entity.getProcessId();
        int order = 0;
        if (ObjectUtils.isEmpty(variableConfigVos)) {
            return;
        }
        for (ProcessVariableConfig config : variableConfigVos) {
            StoneProcessVariableConfigVo configVo = BeanCopyUtils.copyObj(config, StoneProcessVariableConfigVo.class);
            configVo.setId(null);
            configVo.setProcessId(processId);
            configVo.setOrder(order);
            stoneProcessVariableConfigService.saveVo(configVo);
            order++;
        }
    }

    /**
     * 保存流程配置，全删全增，如果不改版本情况
     */
    private void removeProcessConfig(ProcessDeployEntity entity) {
        String processId = entity.getProcessId();
        //如果是变更版本，不可删除节点配置、
        if (!entity.getChangeVersion()) {
            stoneProcessNodeService.removeByProcessId(processId);
            stoneNodeApproveConfigService.removeByProcessId(processId);
            stoneNodeApproveUserConfigService.removeByProcessId(processId);
            stoneProcessAuthConfigService.removeByProcessId(processId);
            stoneEventConfigService.removeByProcessId(processId);
            stoneProcessVariableConfigService.removeByProcessId(processId);
            stoneProcessFormConfigService.removeByProcessId(processId);
        }
    }


    private void saveNodeConfig(ProcessDeployEntity entity) {
        List<ProcessNodeEntity> nodeEntities = entity.getNodeEntities();
        String processId = entity.getProcessId();
        for (ProcessNodeEntity node : nodeEntities) {
            //保存节点配置
            StoneProcessNodeVo nodeVo = BeanCopyUtils.copyAndInit(node, StoneProcessNodeVo.class);
            nodeVo.setProcessId(processId);
            nodeVo.setId(null);
            stoneProcessNodeService.saveVo(nodeVo);
            //保存表单配置
            List<ProcessFormConfig> formConfigs = node.getFormConfigs();
            if (!ObjectUtils.isEmpty(formConfigs)) {
                int order = 0;
                for (ProcessFormConfig config : formConfigs) {
                    StoneProcessFormConfigVo formConfigVo = BeanCopyUtils.copyObj(config, StoneProcessFormConfigVo.class);
                    formConfigVo.setId(null);
                    formConfigVo.setProcessId(processId);
                    formConfigVo.setNodeId(node.getNodeId());
                    formConfigVo.setOrder(order);
                    stoneProcessFormConfigService.saveVo(formConfigVo);
                    order++;
                }
            }
            //保存变量
            List<ProcessVariableConfig> variableConfigs = node.getVariableConfigs();
            if (!ObjectUtils.isEmpty(variableConfigs)) {
                int order = 0;
                for (ProcessVariableConfig config : variableConfigs) {
                    StoneProcessVariableConfigVo configVo = BeanCopyUtils.copyObj(config, StoneProcessVariableConfigVo.class);
                    configVo.setId(null);
                    configVo.setProcessId(processId);
                    configVo.setOrder(order);
                    stoneProcessVariableConfigService.saveVo(configVo);
                    order++;
                }
            }

            //保存事件配置
            List<ProcessEventConfig> eventConfigs = node.getEventConfigs();
            if (!ObjectUtils.isEmpty(eventConfigs)) {
                int order = 0;
                for (ProcessEventConfig eventConfig : eventConfigs) {
                    StoneEventConfigVo eventConfigVo = BeanCopyUtils.copyObj(eventConfig, StoneEventConfigVo.class);
                    eventConfigVo.setId(null);
                    eventConfigVo.setProcessId(processId);
                    eventConfigVo.setEventId(IdUtil.fastUUID());
                    eventConfigVo.setNodeId(node.getNodeId());
                    eventConfigVo.setType(TYPE_NODE);
                    eventConfigVo.setOrder(order);
                    if (ObjectUtils.isEmpty(eventConfigVo.getEventId())) {
                        eventConfigVo.setEventId(PlatUtils.uuid());
                    }
                    stoneEventConfigService.saveVo(eventConfigVo);

                    List<NodeApproveUserConfig> userConfigList = eventConfig.getUserConfigList();
                    if(!ObjectUtils.isEmpty(userConfigList)){
                        List<StoneNodeApproveUserConfigVo> userConfigVos = BeanCopyUtils.copyObjList(userConfigList, StoneNodeApproveUserConfigVo.class);
                        int i = 0;
                        for (StoneNodeApproveUserConfigVo userConfig : userConfigVos) {
                            userConfig.setId(null);
                            userConfig.setEventId(eventConfigVo.getEventId());
                            userConfig.setProcessId(processId);
                            userConfig.setOrder(i);
                            i++;
                        }
                        stoneNodeApproveUserConfigService.saveBatchVoList(userConfigVos);
                    }
                    order++;
                }
            }
            //保存权限配置
            List<NodeAuthConfig> authConfigs = node.getAuthConfigs();
            if (!ObjectUtils.isEmpty(authConfigs)) {
                int order = 0;
                for (NodeAuthConfig authConfig : authConfigs) {
                    StoneProcessAuthConfigVo authConfigVo = BeanCopyUtils.copyObj(authConfig, StoneProcessAuthConfigVo.class);
                    authConfigVo.setId(null);
                    authConfigVo.setProcessId(processId);
                    authConfigVo.setNodeId(node.getNodeId());
                    authConfigVo.setAuthType(TYPE_NODE);
                    authConfigVo.setOrder(order);
                    stoneProcessAuthConfigService.saveVo(authConfigVo);
                    order++;
                }
            }
            //审批配置
            NodeApproveConfig approveConfig = node.getApproveConfig();
            if (ObjectUtils.isEmpty(approveConfig)) {
                continue;
            }
            StoneNodeApproveConfigVo approveConfigVo = BeanCopyUtils.copyObj(approveConfig, StoneNodeApproveConfigVo.class);
            approveConfigVo.setId(null);
            approveConfigVo.setProcessId(processId);
            approveConfigVo.setNodeId(nodeVo.getNodeId());
            if (ObjectUtils.isEmpty(approveConfigVo.getConfigId())) {
                approveConfigVo.setConfigId(PlatUtils.uuid());
            }
            stoneNodeApproveConfigService.saveVo(approveConfigVo);
            //审批人配置
            List<NodeApproveUserConfig> userConfigList = approveConfig.getUserConfigList();
            if (!ObjectUtils.isEmpty(userConfigList)) {
                int order = 0;
                for (NodeApproveUserConfig userConfig : userConfigList) {
                    StoneNodeApproveUserConfigVo userConfigVo = BeanCopyUtils.copyObj(userConfig, StoneNodeApproveUserConfigVo.class);
                    userConfigVo.setId(null);
                    userConfigVo.setConfigId(approveConfigVo.getConfigId());
                    userConfigVo.setProcessId(processId);
                    userConfigVo.setOrder(order);
                    stoneNodeApproveUserConfigService.saveVo(userConfigVo);
                    order++;
                }
            }
            //条件审批人配置
            List<NodeApproveUserConfig> conditionTrueUserConfigList = approveConfig.getConditionTrueUserConfigList();
            if (!ObjectUtils.isEmpty(conditionTrueUserConfigList)) {
                int order = 0;
                for (NodeApproveUserConfig userConfig : conditionTrueUserConfigList) {
                    StoneNodeApproveUserConfigVo userConfigVo = BeanCopyUtils.copyObj(userConfig, StoneNodeApproveUserConfigVo.class);
                    userConfigVo.setId(null);
                    userConfigVo.setConfigId(approveConfigVo.getConfigId());
                    userConfigVo.setProcessId(processId);
                    userConfigVo.setOrder(order);
                    stoneNodeApproveUserConfigService.saveVo(userConfigVo);
                    order++;
                }
            }
            List<NodeApproveUserConfig> conditionFalseUserConfigList = approveConfig.getConditionFalseUserConfigList();
            if (!ObjectUtils.isEmpty(conditionFalseUserConfigList)) {
                int order = 0;
                for (NodeApproveUserConfig userConfig : conditionFalseUserConfigList) {
                    StoneNodeApproveUserConfigVo userConfigVo = BeanCopyUtils.copyObj(userConfig, StoneNodeApproveUserConfigVo.class);
                    userConfigVo.setId(null);
                    userConfigVo.setConfigId(approveConfigVo.getConfigId());
                    userConfigVo.setProcessId(processId);
                    userConfigVo.setOrder(order);
                    stoneNodeApproveUserConfigService.saveVo(userConfigVo);
                    order++;
                }
            }

        }
    }

    /**
     * 保存流程权限配置
     */
    private void saveProcessAuthConfig(ProcessDeployEntity entity) {
        List<NodeAuthConfig> authConfigs = entity.getAuthConfigs();
        String processId = entity.getProcessId();
        if (!ObjectUtils.isEmpty(authConfigs)) {
            int order = 0;
            for (NodeAuthConfig config : authConfigs) {
                StoneProcessAuthConfigVo configVo = BeanCopyUtils.copyObj(config, StoneProcessAuthConfigVo.class);
                configVo.setId(null);
                configVo.setProcessId(processId);
                configVo.setAuthType(TYPE_PROCESS);
                configVo.setOrder(order);
                stoneProcessAuthConfigService.saveVo(configVo);
                order++;
            }
        }
    }


    /**
     * 保存流程事件
     */
    private void saveProcessEvent(ProcessDeployEntity entity) {
        List<ProcessEventConfig> eventConfigs = entity.getEventConfigs();
        String processId = entity.getProcessId();
        if (!ObjectUtils.isEmpty(eventConfigs)) {
            int order = 0;
            for (ProcessEventConfig config : eventConfigs) {
                StoneEventConfigVo configVo = BeanCopyUtils.copyObj(config, StoneEventConfigVo.class);
                configVo.setId(null);
                configVo.setProcessId(processId);
                configVo.setType(TYPE_PROCESS);
                if (ObjectUtils.isEmpty(configVo.getEventId())) {
                    configVo.setEventId(PlatUtils.uuid());
                }
                if (EVENT_OPERATE_TYPE_READ_NODE.equals(configVo.getEventType())) {
                    //如果为待阅
                    List<NodeApproveUserConfig> userConfigList = config.getUserConfigList();
                    if (!ObjectUtils.isEmpty(userConfigList)) {
                        int configOrder = 0;
                        for (NodeApproveUserConfig userConfig : userConfigList) {
                            StoneNodeApproveUserConfigVo userConfigVo = BeanCopyUtils.copyObj(userConfig, StoneNodeApproveUserConfigVo.class);
                            userConfigVo.setId(null);
                            userConfigVo.setProcessId(processId);
                            userConfigVo.setEventId(configVo.getEventId());
                            userConfigVo.setOrder(configOrder);
                            stoneNodeApproveUserConfigService.saveVo(userConfigVo);
                            order++;
                        }
                    }
                }
                configVo.setOrder(order);
                stoneEventConfigService.saveVo(configVo);
                order++;
            }
        }
    }


    private void saveProcess(ProcessDeployEntity entity) {
        StoneProcessVo processVo = entity.getProcessVo();
        String processCode = processVo.getProcessCode();
        AssertUtils.isTrue(!ObjectUtils.isEmpty(processCode), "流程编号不可为空");
        AssertUtils.isTrue(!ObjectUtils.isEmpty(processVo.getProcessVersion()), "流程版本不可为空");
        entity.setChangeVersion(Boolean.FALSE);
        if (ObjectUtils.isEmpty(processVo.getClassId())) {
            processVo.setClassId(entity.getClassId());
        }
        if (ObjectUtils.isEmpty(processVo.getProcessId())) {
            //新增
            addProcessVo(entity);
        } else {
            //更新
            updateProcessVo(entity);
        }
    }

    /**
     * 添加流程信息
     */
    private void addProcessVo(ProcessDeployEntity entity) {
        StoneProcessVo processVo = entity.getProcessVo();
        String processCode = processVo.getProcessCode();
        String content = xmlContent(entity);
        StoneProcessVo old = stoneProcessService.getByProcessCode(processCode);
        if (!ObjectUtils.isEmpty(old)) {
            throw new WorkflowException("流程" + processCode + "已存在");
        }
        processVo.setProcessId(PlatUtils.uuid());
        processVo.setNewVersionFlag(FLAG_TRUE);
        //流程内容
        ProcessBuilder builder = ProcessBuilderFactory.getBuilder(content, entity);
        builder.buildProcess(processVo.getProcessId());
        entity.setProcessId(processVo.getProcessId());
        processVo.setContent(content.getBytes(StandardCharsets.UTF_8));
        processVo.setStatus(ProcessStatusEnum.DRAFT.getType());
        entity.setProcessVo(stoneProcessService.saveVo(processVo));
    }

    private String xmlContent(ProcessDeployEntity entity) {
        FlowProcess process = entity.getProcess();
        return new ParseJsonToXmlCmd(process).execute();
    }

    /**
     * 更新流程信息
     */
    private void updateProcessVo(ProcessDeployEntity entity) {
        StoneProcessVo processVo = entity.getProcessVo();
        String content = xmlContent(entity);
        AssertUtils.isTrue(!ObjectUtils.isEmpty(content), "流程内容不可为空");
        String processCode = processVo.getProcessCode();
        String processId = processVo.getProcessId();
        StoneProcessVo oldProcessVo = stoneProcessService.getByProcessId(processId);
        //processCode不可修改
        if (!processCode.equals(oldProcessVo.getProcessCode())) {
            throw new WorkflowException("流程编号不可修改");
        }
        //如果是旧版本，不可修改版本号
        if (FLAG_FALSE.equals(oldProcessVo.getNewVersionFlag())
                && !processVo.getProcessVersion().equals(oldProcessVo.getProcessVersion())) {
            throw new BusinessException("旧版流程版本号不可修改");
        }
        //判断版本是否变更，如果版本变更则新建流程原流程不变
        if (!oldProcessVo.getProcessVersion().equals(processVo.getProcessVersion())) {
            processVo = BeanCopyUtils.copyAndInit(processVo, StoneProcessVo.class);
            //新建流程pid需用新的
            processVo.setProcessId(PlatUtils.uuid());
            processVo.setNewVersionFlag(FLAG_TRUE);
            entity.setChangeVersion(Boolean.TRUE);
            oldProcessVo.setNewVersionFlag(FLAG_FALSE);
            stoneProcessService.saveVo(oldProcessVo);
        }
        //流程内容
        ProcessBuilder builder = ProcessBuilderFactory.getBuilder(content, entity);
        builder.buildProcess(processVo.getProcessId());
        entity.setProcessId(processVo.getProcessId());
        processVo.setContent(content.getBytes(StandardCharsets.UTF_8));
        entity.setProcessVo(stoneProcessService.saveVo(processVo));
    }
}
